#ifndef __SEM_HPP__
#define __SEM_HPP__

#include <iostream>
#include <string>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>
#include <sys/stat.h>

// 对信号量的接口进行封装， 包括信号量的创建、等待、释放等操作

const std::string defaultpath_name = "/tmp";
const int defaultproj_id = 0x122;

const int SEM_CREATE = IPC_CREAT | IPC_EXCL | 0666;
const int SEM_GET = IPC_CREAT;

class Semaphore
{
    // 用户指定是进行创建还是获取信号量
    void CG_Sem(int key, int flag)
    {
        semid_ = semget(key, 1, flag);
        if (semid_ < 0)
        {
            std::cerr << "semget error" << " , int file" << __FILE__ << " , int line" << __LINE__ << std::endl;
            exit(2);
        }
    }

    key_t Get_Key(const std::string &path_name, const int &proj_id)
    {
        key_t key = ftok(path_name.c_str(), proj_id);
        if (key < 0)
        {
            std::cerr << "ftok error" << " , int file" << __FILE__ << " , int line" << __LINE__ << std::endl;
            exit(1);
        }
        return key;
    }

    void Init_Sem()
    {
        union semun
        {
            int val;               /* Value for SETVAL */
            struct semid_ds *buf;  /* Buffer for IPC_STAT, IPC_SET */
            unsigned short *array; /* Array for GETALL, SETALL */
            struct seminfo *__buf; /* Buffer for IPC_INFO
                                      (Linux-specific) */
        };
        union semun arg;
        arg.val = 1;
        int n = semctl(semid_, 0, SETVAL, arg);
        if (n < 0)
        {   
            std::cerr << "semctl error" << " , int file" << __FILE__ << " , int line" << __LINE__ << std::endl;
            exit(3);
        }
    }

    void PV(int flag)
    {
        struct sembuf sbf;
        sbf.sem_num = 0;
        sbf.sem_op = flag;
        sbf.sem_flg = SEM_UNDO;
        int n = semop(semid_, &sbf, 1);
        if (n < 0)
        {
            std::cerr << "semop error" << " , int file : " << __FILE__ << " , int line : " << __LINE__ << std::endl;
            exit(5);
        }
    }

public:
    // 创建信号量
    void Creat_Sem(const std::string &path_name = defaultpath_name, const int &proj_id = defaultproj_id)
    {
        key_t key = Get_Key(path_name, proj_id);
        CG_Sem(key,SEM_CREATE);
        // 初始化信号量
        Init_Sem();
    }

    // 获取信号量
    void Get_Sem(const std::string &path_name = defaultpath_name, const int &proj_id = defaultproj_id)
    {
        key_t key = Get_Key(path_name, proj_id);
        CG_Sem(key, SEM_GET);
    }

    // P操作，申请一个信号量
    void P()
    {
        PV(-1);
    }
    void V()
    {
        PV(1);
    }

    // 销毁信号量
    void Destory_Sem()
    {
        int n = semctl(semid_ , 0 , IPC_RMID);
        if(n < 0)
        {
            std::cerr << "semctl error" << " , int file" << __FILE__ << " , int line" << __LINE__ << std::endl;
            exit(6);
        }
    }

private:
    int semid_;
};

#endif